package com.sky.service.impl;

import com.sky.dto.GoodsSalesDTO;
import com.sky.entity.Orders;
import com.sky.mapper.OrderMapper;
import com.sky.mapper.UserMapper;
import com.sky.service.ReportService;
import com.sky.service.WorkSpaceService;
import com.sky.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ReportServiceImpl implements ReportService {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private WorkSpaceService workSpaceService;

    public static List<LocalDate> dateList = null;

    /**
     * 营业额统计
     * @param begin
     * @param end
     * @return
     */
    @Override
    public TurnoverReportVO getTurnoverStatistic(LocalDate begin, LocalDate end) {
        // 1.创建集合用于存放从begin到end中每天的日期
        dateList = new ArrayList<>();
        // 将begin到end中的每天的日期存进dateList中
        saveTheDatesInList(begin, end);

        // 2.创建集合用于存放每一天的营业额(记录的是传进来的时间内，订单状态为“已完成”的订单)
        List<Double> turnoverList = new ArrayList<>();
        for (LocalDate date : dateList) {
            // LocalDateTime.of(date, LocalTime.MIN)的作用是将date日期从年-月-日变成年-月-日 00:00:00
            LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
            // LocalDateTime.of(date, LocalTime.MAX)的作用是将date日期从年-月-日变成年-月-日 59:59:59
            LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);

            // 将上述三个要传进去的变量放进map集合中,开始时间，结束时间，订单状态为已完成
            Map map = new HashMap();
            map.put("begin", beginTime);
            map.put("end", endTime);
            map.put("status", Orders.COMPLETED);
            // 查询出当天营业额
            Double turnover = orderMapper.getSumByMap(map);
            // 如果当天没有订单，那么营业额将会变成空，所以我们要手动设置为0.0
            turnover = (turnover == null ? 0.0 : turnover);
            // 将每天的营业额存放进turnoverList中
            turnoverList.add(turnover);
        }

        // 3.将dateList与turnoverList中的数据用逗号分隔开
        String date = StringUtils.join(dateList, ",");
        String turnover = StringUtils.join(turnoverList, ",");

        // 4.将date与turnover存放进TurnoverReportVO中去
        TurnoverReportVO turnoverReportVO = TurnoverReportVO.builder()
                .dateList(date)
                .turnoverList(turnover)
                .build();

        return turnoverReportVO;
    }

    /**
     * 用户统计
     * @param begin
     * @param end
     * @return
     */
    @Override
    public UserReportVO getUserStatistic(LocalDate begin, LocalDate end) {
        // 1.创建集合用于存放从begin到end中每天的日期
        dateList = new ArrayList<>();
        // 将begin到end中的每天的日期存进dateList中
        saveTheDatesInList(begin, end);

        // 2.创建集合用存放于用户总数以及新增用户数量
        List<Integer> userTotals = new ArrayList<>();
        List<Integer> newUsers = new ArrayList<>();
        for (LocalDate date : dateList) {
            LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
            LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);

            // 将上述三个要传进去的变量放进map集合中,开始时间，结束时间,并查询
            Map map = new HashMap();
            map.put("end", endTime);
            Integer userTotal = userMapper.countByMap(map);
            map.put("begin", beginTime);
            Integer newUser = userMapper.countByMap(map);

            // 将上述查询出来的两个变量分别插入到集合中
            userTotals.add(userTotal);
            newUsers.add(newUser);
        }

        // 3.将date与userTotals以及newUsers中的数据用逗号分隔开
        String date = StringUtils.join(dateList, ",");
        String userTotal = StringUtils.join(userTotals, ",");
        String newUser = StringUtils.join(newUsers, ",");

        // 4.将userTotal与newUser存放进UserReportVO中去
        UserReportVO userReportVO = UserReportVO.builder()
                .dateList(date)
                .newUserList(newUser)
                .totalUserList(userTotal)
                .build();

        return userReportVO;
    }

    /**
     * 订单统计
     * @param begin
     * @param end
     * @return
     */
    @Override
    public OrderReportVO getOrderStatistic(LocalDate begin, LocalDate end) {
        // 1.创建集合用于存放从begin到end中每天的日期
        dateList = new ArrayList<>();
        // 将begin到end中的每天的日期存进dateList中
        saveTheDatesInList(begin, end);

        // 2.创建集合用于存放每日订单数以及每日有效订单数
        List<Integer> orderCountList = new ArrayList<>();
        List<Integer> validOrderCountList = new ArrayList<>();
        for (LocalDate date : dateList) {
            LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
            LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);

            Map map = new HashMap();
            map.put("begin", beginTime);
            map.put("end", endTime);
            Integer orderCount = orderMapper.countOrdersByMap(map);
            map.put("status", Orders.COMPLETED);
            Integer validOrderCount = orderMapper.countOrdersByMap(map);

            orderCountList.add(orderCount);
            validOrderCountList.add(validOrderCount);
        }

        // 3.计算时间区间内订单总数量与有效订单总数量
        /*stream().reduce(Integer::sum).get()的作用是将集合中的元素遍历一遍，然后进行求和计算，最后get获取该值*/
        Integer orderCount = orderCountList.stream().reduce(Integer::sum).get();
        Integer validOrderCount = validOrderCountList.stream().reduce(Integer::sum).get();

        // 4.计算订单完成率: 有效订单 / 总订单
        Double orderCompletionRate = 0.0;
        // 总订单有时候会是0，为了防止异常，所以要判断总订单是否为0
        if(orderCount != 0){
            orderCompletionRate = validOrderCount.doubleValue() / orderCount;
        }

        // 5.将以上六个数据存放进OrderReportVO中去
        OrderReportVO orderReportVO = OrderReportVO.builder()
                .dateList(StringUtils.join(dateList, ","))
                .orderCountList(StringUtils.join(orderCountList, ","))
                .validOrderCountList(StringUtils.join(validOrderCountList, ","))
                .totalOrderCount(orderCount)
                .validOrderCount(validOrderCount)
                .orderCompletionRate(orderCompletionRate)
                .build();

        return orderReportVO;
    }

    private void saveTheDatesInList(LocalDate begin, LocalDate end) {
        while(!begin.equals(end.plusDays(1))){
            dateList.add(begin);
            // 日期计算，计算指定日期的后一天
            begin = begin.plusDays(1);
        }
    }

    /**
     * 销量排名top10统计
     * @param begin
     * @param end
     * @return
     */
    @Override
    public SalesTop10ReportVO getSalesTop10Statistic(LocalDate begin, LocalDate end) {
        // 1.将前端传过来的begin和end转成localDatetime类型
        LocalDateTime beginTime = LocalDateTime.of(begin, LocalTime.MIN);
        LocalDateTime endTime = LocalDateTime.of(end, LocalTime.MAX);

        // 2.查询销量top10的名称及数量封装进GoodsSalesDTO的集合中
        List<GoodsSalesDTO> salesTop10List = orderMapper.getSalesTop10ByDate(beginTime, endTime);

        // 3.将salesTop10List中的name和number遍历出来
        List<String> names = salesTop10List.stream().map(GoodsSalesDTO::getName).collect(Collectors.toList());
        List<Integer> numbers = salesTop10List.stream().map(GoodsSalesDTO::getNumber).collect(Collectors.toList());

        // 4.将上述遍历出来的names和numbers封装进SalesTop10ReportVO
        // 现将上述两个集合中的元素分别用逗号分隔开
        String nameList = StringUtils.join(names, ",");
        String numberList = StringUtils.join(numbers, ",");
        SalesTop10ReportVO salesTop10ReportVO = SalesTop10ReportVO.builder()
                .nameList(nameList)
                .numberList(numberList)
                .build();

        return salesTop10ReportVO;
    }

    /**
     * 导出Excel报表
     * @param response
     */
    @Override
    public void getBusinessDate(HttpServletResponse response) {
        // 1.查询数据库，获取近30天的运营数据
        // 先获取时间 从30天前到昨天这个时间段
        LocalDate beginTime = LocalDate.now().minusDays(30);
        LocalDate endTime = LocalDate.now().minusDays(1);
        // 获取精确到时分秒的时间段
        LocalDateTime begin = LocalDateTime.of(beginTime, LocalTime.MIN);
        LocalDateTime end = LocalDateTime.of(endTime, LocalTime.MAX);
        // 通过workSpaceService接口中的getBusinessData获取运营数据(查询出excel报表中的概览数据)
        BusinessDataVO businessData = workSpaceService.getBusinessData(begin, end);

        // 2.通过POI将上述查询到的数据写入Excel文件中
        // 先获取一个读取resource目录下的运营数据报表模板的输入流
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("template/运营数据报表模板.xlsx");

        try {

            // 根据这个输入流来创建一个新的excel报表
            XSSFWorkbook excel = new XSSFWorkbook(inputStream);

            // 根据名称获取表格sheet的标签页
            XSSFSheet sheet = excel.getSheet("Sheet1");

            // 根据表格获取第二行(下标为1)的第二个单元格(下标为1)来进行填充信息(概览数据),以此类推
            sheet.getRow(1).getCell(1).setCellValue(beginTime + "   " + endTime);
            sheet.getRow(3).getCell(2).setCellValue(businessData.getTurnover());
            sheet.getRow(3).getCell(4).setCellValue(businessData.getOrderCompletionRate());
            sheet.getRow(3).getCell(6).setCellValue(businessData.getNewUsers());
            sheet.getRow(4).getCell(2).setCellValue(businessData.getValidOrderCount());
            sheet.getRow(4).getCell(4).setCellValue(businessData.getUnitPrice());

            // 同上填充明细数据
            for (int i = 0; i < 30; i++) {
                // 先查询出每一天的明细数据信息
                LocalDate date = beginTime.plusDays(i);
                BusinessDataVO businessDetailData = workSpaceService.getBusinessData(LocalDateTime.of(date, LocalTime.MIN), LocalDateTime.of(date, LocalTime.MAX));

                sheet.getRow(7 + i).getCell(1).setCellValue(date.toString());
                sheet.getRow(7 + i).getCell(2).setCellValue(businessDetailData.getTurnover() == null ? "无" : businessDetailData.getTurnover().toString());
                sheet.getRow(7 + i).getCell(3).setCellValue(businessDetailData.getValidOrderCount());
                sheet.getRow(7 + i).getCell(4).setCellValue(businessDetailData.getOrderCompletionRate() == null ? "0" : businessDetailData.getOrderCompletionRate().toString());
                sheet.getRow(7 + i).getCell(5).setCellValue(businessDetailData.getUnitPrice());
                sheet.getRow(7 + i).getCell(6).setCellValue(businessDetailData.getNewUsers());
            }

            // 3.通过输出流将Excel文件下载到客户端浏览器
            ServletOutputStream outputStream = response.getOutputStream();
            excel.write(outputStream);

            // 4.关闭流
            outputStream.close();
            excel.close();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}
